package utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import org.biojava.bio.structure.AminoAcid;
import org.biojava.bio.structure.Chain;
import org.biojava.bio.structure.Group;
import org.biojava.bio.structure.Structure;
import rotlib.RotamerLibrary;
import rotlib.RotamerLibraryCollection;
import rotlib.io.RotamerLibraryReader;
import utils.StructureUtil;

/**
 * @author clezcano
 */

public class Solution {
    private final static double EPSILON  = 0.00001;
    private int lastChangedResidue1 = -1, lastChangedResidue2 = -1;
    private ArrayList<HashMap<Integer,String>> RotamerVector = null;
    private ArrayList<HashMap<Integer,String>> AAVector = null;
    //Optmizar se vuelve a cargar ? la libreria 
    private RotamerLibraryCollection rlc = RotamerLibraryReader.readRotamerLibrary(RotamerLibraryCollection.DUN_IND_ROT_LIB_TYPE);
    private Structure s_pdb = null;
    public double energy = -1, vdWPotential = -1, cRatio = -1, frequency = -1;
    
   

    public Solution() {}

    public Solution(Structure s1) {
        Chain c;
        Group g;
        AminoAcid aa1;
        int aminoAcidIndex;
        String pdbName;

        vdWPotential = -1;
        cRatio = -1;
        frequency = -1;
        energy = -1;
        lastChangedResidue1 = -1;
        lastChangedResidue2 = -1;
        s_pdb = s1;
        System.out.println(s1.getChains().size());
        RotamerVector = new ArrayList(s1.getChains().size());
        AAVector = new ArrayList(s1.getChains().size());
        for (int chain = 0; s1 != null && s1.getChains() != null && chain < s1.getChains().size(); chain++) {
            c = s1.getChain(chain);
            RotamerVector.add(new HashMap<Integer,String>());
            AAVector.add(new HashMap<Integer,String>());
            for (int group = 0; c != null && c.getAtomGroups() != null && group < c.getAtomGroups().size(); group++) {
                g = c.getAtomGroup(group);
                if (g instanceof AminoAcid) {
                    aa1 = (AminoAcid) g;
                    pdbName = aa1.getPDBName();
                    aminoAcidIndex=Integer.parseInt(aa1.getPDBCode())-1;
                    RotamerVector.get(chain).put(aminoAcidIndex,"-1");
                    AAVector.get(chain).put(aminoAcidIndex,pdbName);
                    //aminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa1.getPDBName());
                }
            }
       }
    }

    public ArrayList<HashMap<Integer,String>> getRomaterVector() {
        return RotamerVector;
    }
    
    public ArrayList<HashMap<Integer,String>> getAAVector() {
        return AAVector;
    }

    public Solution CopySolution() {
        Solution sol = new Solution();
        sol.s_pdb = s_pdb;
        sol.RotamerVector = new ArrayList(RotamerVector.size());
        sol.AAVector = new ArrayList(AAVector.size());
        sol.lastChangedResidue1 = lastChangedResidue1;
        sol.lastChangedResidue2 = lastChangedResidue2;
        sol.vdWPotential = vdWPotential;
        sol.cRatio = cRatio;
        sol.frequency = frequency;
        sol.energy = energy;
        int group=0;
        for (int chain = 0; chain < AAVector.size(); chain++) {
            sol.RotamerVector.add(new HashMap<Integer,String>());
            sol.AAVector.add(new HashMap<Integer,String>());
            Object[] keys=AAVector.get(chain).keySet().toArray();
            Arrays.sort(keys);
            for (int key = 0; key < keys.length; key++) {
                group=Integer.parseInt(keys[key].toString());                      ;
                sol.RotamerVector.get(chain).put(group, RotamerVector.get(chain).get(group));
                sol.AAVector.get(chain).put(group, AAVector.get(chain).get(group));
                //AAVector.get(chain).add(pdbName);
                    //aminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa1.getPDBName());
            }
        }
        return sol;
    }
    
    public double getAccuracy() {
        if (s_pdb == null) {
           System.out.println("PDB in Solution is null");
        }

        return 1/((StructureUtil.AbsoluteAccuracy(s_pdb, StructureUtil.createStructure(s_pdb, this, rlc), "X1") +
                   StructureUtil.AbsoluteAccuracy(s_pdb, StructureUtil.createStructure(s_pdb, this, rlc), "X12") +
                   StructureUtil.AbsoluteAccuracy(s_pdb, StructureUtil.createStructure(s_pdb, this, rlc), "X123")
                  )/3);
    }

    public void getRotamerFrequency() {
        double sum = 0.0, gamma, rotamerProb;
        RotamerLibrary rl;

        for (int chain = 0; chain < AAVector.size(); chain++) {
            for (int group = 0; group < AAVector.get(chain).size(); group++) {
                //se valida los aminoacidos GLY y ALA, por que no tienen angulos de torsion en la cadena lateral
                if (!(AAVector.get(chain).get(group).equalsIgnoreCase("GLY") || AAVector.get(chain).get(group).equalsIgnoreCase("ALA"))) {
                    rl = rlc.getRotamerLibrary(AAVector.get(chain).get(group));
                    if (AAVector.get(chain).get(group).equalsIgnoreCase("PHE") || AAVector.get(chain).get(group).equalsIgnoreCase("TYR") || AAVector.get(chain).get(group).equalsIgnoreCase("TRP") || AAVector.get(chain).get(group).equalsIgnoreCase("HIS")) {
                        gamma = 2.0;
                    } else {
                        gamma = 1.0;
                    }

                    rotamerProb = rl.getRotamer().get(Integer.parseInt(RotamerVector.get(chain).get(group))).getProbabilityValue();
                    if(equals(rotamerProb, 0) == true) {
                        rotamerProb = 0.001;
                    }
                    //sum += gamma * Math.log10(rotamerProb/MaxRotamerProb(rl));
                    sum += gamma * Math.log10(rotamerProb / MaxRotamerProb(AAVector.get(chain).get(group)));
                }
            }
        }
        this.frequency = -sum;
    }

    private double MaxRotamerProb(String AminoName) {
                double maxPro;
                if (AminoName.equalsIgnoreCase("ARG")) {
                   maxPro = 10.99;
                }
                else if (AminoName.equalsIgnoreCase("ASN")) {
                   maxPro = 28.20;
                }
                else if (AminoName.equalsIgnoreCase("ASP")) {
                   maxPro = 30.87;
                }
                else if (AminoName.equalsIgnoreCase("CYS")) {
                   maxPro = 56.64;
                }
                else if (AminoName.equalsIgnoreCase("GLN")) {
                   maxPro = 18.53;
                }
                else if (AminoName.equalsIgnoreCase("GLU")) {
                   maxPro = 18.84;
                }
                else if (AminoName.equalsIgnoreCase("HIS")) {
                   maxPro = 29.64;
                }
                else if (AminoName.equalsIgnoreCase("ILE")) {
                   maxPro = 61.58;
                }
                else if (AminoName.equalsIgnoreCase("LEU")) {
                   maxPro = 62.52;
                }
                else if (AminoName.equalsIgnoreCase("LYS")) {
                   maxPro = 22.79;
                }
                else if (AminoName.equalsIgnoreCase("MET")) {
                   maxPro = 18.53;
                }
                else if (AminoName.equalsIgnoreCase("PHE")) {
                   maxPro = 47.08;
                }
                else if (AminoName.equalsIgnoreCase("PRO")) {
                   maxPro = 50.72;
                }
                else if (AminoName.equalsIgnoreCase("SER")) {
                   maxPro = 47.07;
                }
                else if (AminoName.equalsIgnoreCase("THR")) {
                   maxPro = 48.86;
                }
                else if (AminoName.equalsIgnoreCase("TRP")) {
                   maxPro = 30.81;
                }
                else if (AminoName.equalsIgnoreCase("TYR")) {
                   maxPro = 47.35;
                }
                else if (AminoName.equalsIgnoreCase("VAL")) {
                   maxPro = 73.90;
                }
                else {
                   maxPro = 0;
                }

                return maxPro;
    }

    public void setMaxProb() {
        String r = "";
        Object[] keys;
        int group;
        for (int chain = 0; chain < AAVector.size(); chain++) {
            keys=AAVector.get(chain).keySet().toArray(new Integer[0]);
            Arrays.sort(keys);
            for (int key = 0; key < AAVector.get(chain).size(); key++) {
                group=Integer.parseInt(keys[key].toString());
                if (AAVector.get(chain).get(group).equalsIgnoreCase("ARG")) {
                   r = "56";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("ASN")) {
                   r = "14";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("ASP")) {
                   r = "7";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("CYS")) {
                   r = "2";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("GLN")) {
                   r = "29";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("GLU")) {
                   r = "22";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("HIS")) {
                   r = "8";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("ILE")) {
                   r = "7";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("LEU")) {
                   r = "7";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("LYS")) {
                   r = "55";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("MET")) {
                   r = "25";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("PHE")) {
                   r = "4";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("PRO")) {
                   r = "0";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("SER")) {
                   r = "0";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("THR")) {
                   r = "0";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("TRP")) {
                   r = "8";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("TYR")) {
                   r = "4";
                }
                else if (AAVector.get(chain).get(group).equalsIgnoreCase("VAL")) {
                   r = "1";
                }
                else {
                   r = "-1";
                }
                RotamerVector.get(chain).put(group, r);
            }
        }
    }
    public static boolean equals(double a, double b) {
        return Math.abs(a - b) < EPSILON;
    }
}
